/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.generator.builder;

/**
 *
 * @author 45581126
 */
public class JarManager {
    protected static interface Node {
        void add(String path, String[] parts, int index, java.io.File file);
        void createEntry(java.util.jar.JarOutputStream stream) throws java.io.IOException;
    }
    protected static class FileNode implements Node {
        protected String p_Path;
        protected java.io.File p_File;
        public FileNode(String path, java.io.File file) {
            p_Path = path;
            p_File = file;
        }
        @Override public void add(String path, String[] parts, int index, java.io.File file) {}
        @Override public void createEntry(java.util.jar.JarOutputStream stream) throws java.io.IOException {
            java.io.FileInputStream filestream = new java.io.FileInputStream(p_File);
            java.io.BufferedInputStream bufferedstream = new java.io.BufferedInputStream(filestream);
            java.util.jar.JarEntry fileEntry = new java.util.jar.JarEntry(p_Path);
            stream.putNextEntry(fileEntry);
            byte[] data = new byte[1024];
            int byteCount;
            while ((byteCount = bufferedstream.read(data, 0, 1024)) > -1) {
                stream.write(data, 0, byteCount);
            }
            bufferedstream.close();
            filestream.close();
        }
    }
    public static class DirNode implements Node {
        protected String p_Path;
        protected java.util.HashMap<String, Node> p_Children;
        public DirNode(String path) {
            p_Path = path;
            p_Children = new java.util.HashMap<String, Node>();
        }
        public void add(String path) { add(path, path.split("/"), 0, null); }
        public void add(String path, java.io.File file) { add(path, path.split("/"), 0, file); }
        @Override public void add(String path, String[] parts, int index, java.io.File file) {
            String current = parts[index];
            if (index == (parts.length -1)) {
                if (current.isEmpty())
                    return;
                if (p_Children.containsKey(current))
                    return;
                Node child = null;
                if (file == null) child = new DirNode(path);
                else child = new FileNode(path, file);
                p_Children.put(current, child);
            } else {
                if (!p_Children.containsKey(current))
                    p_Children.put(current, new DirNode(p_Path + current + "/"));
                p_Children.get(current).add(path, parts, index + 1, file);
            }
        }
        @Override public void createEntry(java.util.jar.JarOutputStream stream) throws java.io.IOException {
            if (!p_Path.isEmpty()) {
                java.util.jar.JarEntry fileEntry = new java.util.jar.JarEntry(p_Path);
                stream.putNextEntry(fileEntry);
            }
            for (Node child : p_Children.values())
                child.createEntry(stream);
        }
    }

    protected java.io.FileOutputStream p_Output;
    protected java.util.jar.JarOutputStream p_JarStream;
    protected java.util.Map<String, String> p_Attributes;
    protected String p_ManifestVersion;
    protected String p_MainClass;
    protected String p_ClassPath;
    protected DirNode p_Root;

    public java.util.Map<String, String> getAttributes() { return p_Attributes; }
    public String getManifestVersion() { return p_ManifestVersion; }
    public String getMainClass() { return p_MainClass; }
    public String getClassPath() { return p_ClassPath; }
    public void setManifestVersion(String version) { p_ManifestVersion = version; }
    public void setMainClass(String className) { p_MainClass = className; }
    public void setClassPath(String path) { p_ClassPath = path; }
    
    public JarManager() {
        p_Root = new DirNode("");
        p_Attributes = new java.util.HashMap<String, String>();
    }

    public boolean writeTo(String file) {
        try {
            init(file);
            p_Root.createEntry(p_JarStream);
            close();
        } catch (java.io.IOException ex) {
            ex.printStackTrace();
            return false;
        }
        return true;
    }

    public void addFile(String localPath, String file) {
        p_Root.add(localPath, new java.io.File(file));
    }
    public void addDirectory(String dir, String localPath) {
        make_directory(new java.io.File(dir), localPath, true);
    }
    public void addDirectoryContent(String dir, String localPath) {
        make_directory(new java.io.File(dir), localPath, false);
    }
    private void make_directory(java.io.File dir, String localPath, boolean createEntry) {
        if (createEntry)
            p_Root.add(localPath);
        for (java.io.File element : dir.listFiles()) {
            if (element.isDirectory())
                make_directory(element, localPath + element.getName() + "/", true);
            else
                p_Root.add(localPath + element.getName(), element);
        }
    }


    private void init(String file) throws java.io.IOException {
        p_Output = new java.io.FileOutputStream(file);
        java.util.jar.Manifest manifest = new java.util.jar.Manifest();
        java.util.jar.Attributes manifestAttr = manifest.getMainAttributes();
        //note:Must set Manifest-Version,or the manifest file will be empty!
        if (p_ManifestVersion != null) {
            manifestAttr.putValue("Manifest-Version", p_ManifestVersion);
            if (p_MainClass != null)
                manifestAttr.putValue("Main-Class", p_MainClass);
            if (p_ClassPath != null && !p_ClassPath.equals(""))
                manifestAttr.putValue("Class-Path", p_ClassPath);
            if (p_Attributes != null) {
                for (String key : p_Attributes.keySet())
                    manifestAttr.putValue(key, p_Attributes.get(key));
            }
        }
        p_JarStream = new java.util.jar.JarOutputStream(p_Output, manifest);
    }
    private void close() throws java.io.IOException {
        p_JarStream.flush();
        p_JarStream.close();
        p_Output.close();
        p_JarStream = null;
        p_Output = null;
    }


    public static void makeJar(String dataDir, String outputPath, String manifestVerion, String mainClass, String classPath) throws java.io.IOException {
        JarManager manager = new JarManager();
        manager.setManifestVersion(manifestVerion);
        manager.setMainClass(mainClass);
        manager.setClassPath(classPath);
        manager.addDirectoryContent(dataDir, "");
        manager.writeTo(outputPath);
    }
}
